package org.example.friend.service.question.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.stream.StreamUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.PageHelper;
import org.example.common.core.constants.Constants;
import org.example.common.core.domain.TableDataInfo;
import org.example.friend.domain.question.Question;
import org.example.friend.domain.question.dto.QuestionQueryDTO;
import org.example.friend.domain.question.es.QuestionES;
import org.example.friend.domain.question.vo.QuestionDetailVO;
import org.example.friend.domain.question.vo.QuestionVO;
import org.example.friend.elasticsearch.QuestionRepository;
import org.example.friend.manager.QuestionCacheManager;
import org.example.friend.mapper.question.QuestionMapper;
import org.example.friend.mapper.user.UserSubmitMapper;
import org.example.friend.service.question.QuestionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class QuestionServiceImpl implements QuestionService {
    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private QuestionRepository questionRepository;

    @Autowired
    private QuestionCacheManager questionCacheManager;

    @Autowired
    private UserSubmitMapper userSubmitMapper;


    @Override
    public TableDataInfo list(QuestionQueryDTO queryDTO) {
        //先判断此时ElasticSearch中是否存在数据
        long count = questionRepository.count();
        if (count <= 0) {
            //从数据库中获取数据，并刷新ES的缓存(也就是让数据库的数据和ES进行同步)
            refreshQuestion();
        }

        Integer difficulty = queryDTO.getDifficulty();
        String keyword = queryDTO.getKeyword();
        //构造pageable，使用PageRequest.of()进行，是因为，在findAll方法中会使用到分页
        //Sort.by是规定分页后的排序方式,此处是按照创建时间进行降序排序
        Pageable pageable = PageRequest.of(queryDTO.getPageNum()-1,queryDTO.getPageSize()
                ,Sort.by(Sort.Direction.DESC, "createTime"));
        Page<QuestionES> questionES;
        if (difficulty == null && StrUtil.isEmpty(keyword)) {
            questionES =  questionRepository.findAll(pageable);
        } else if (StrUtil.isEmpty(keyword)) {
            //此时difficulty is not NULL
            questionES =  questionRepository.findQuestionByDifficulty(difficulty, pageable);
        }else if (difficulty == null) {
            //keyword is not NULL
            questionES =  questionRepository.findByTitleOrContent(keyword, keyword, pageable);
        } else {
            //此时都不为NULL
            questionES =  questionRepository.findByTitleOrContentAndDifficulty(keyword, keyword, difficulty, pageable);
        }

        //获取当前Page的总数，如果是0，就是没有数据,getTotalElements是获取全部元素
        long totalElements = questionES.getTotalElements();
        if (totalElements <= 0) {
            TableDataInfo.empty();
        }

        //返回的数据是VO类型需要进行类型转换    questionES.getContent()获取List<questionES> .   此处定义的questionES是Page<QuestionES>,是分页类型
        List<QuestionVO> questionVOS = BeanUtil.copyToList(questionES.getContent(), QuestionVO.class);
        return TableDataInfo.success(questionVOS,totalElements);
    }

    @Override
    public QuestionDetailVO detail(Long questionId) {
        //先从Elasticsearch中进行查询数据
        QuestionDetailVO questionDetailVO = new QuestionDetailVO();
        //如果 questionId 对应的 Question 存在，则返回该 Question 对象。
        //如果不存在，则返回 null。  orElse是Optional的方法不是ES的
        QuestionES questionES = questionRepository.findById(questionId).orElse(null);
        if (questionES != null) {
            BeanUtil.copyProperties(questionES, questionDetailVO);
            return questionDetailVO;
        }
        //如果ES中不存在数据，就先从数据库中进行查询
        Question question = questionMapper.selectById(questionId);
        if (question == null) {
            return null;
        }
        //数据库中存在数据，则需要刷新一下此时作为缓存的ES，把数据放入到ES当中
        refreshQuestion();
        BeanUtil.copyProperties(question, questionDetailVO);
        return questionDetailVO;
    }

    @Override
    public String preQuestion(Long questionId) {
        //先从redis缓存中获取题目的列表
        Long listSize = questionCacheManager.getListSize();
        if (listSize <= 0 || listSize == null) {
            //不存在，则需要通过刷新缓存的方式从数据库中获取信息，并存入缓存中
            questionCacheManager.refreshCache();
        }
        //获取当前题目在redis缓存中的下标
        return questionCacheManager.preQuestion(questionId).toString();
    }

    @Override
    public String nextQuestion(Long questionId) {
        //先从redis缓存中获取题目的列表
        Long listSize = questionCacheManager.getListSize();
        if (listSize <= 0 || listSize == null) {
            //不存在，则需要通过刷新缓存的方式从数据库中获取信息，并存入缓存中
            questionCacheManager.refreshCache();
        }
        //获取当前题目在redis缓存中的下标
        return questionCacheManager.nextQuestion(questionId).toString();
    }

    @Override
    public List<QuestionVO> hotList() {
        //先从redis缓存中查询是否存在热点题目Id列表
        Long total = questionCacheManager.getHotListSize();
        List<Long> questionHotIds;
        if (total <= 0 || total == null) {
            //缓存不存在热点题目Id列表，从数据库中进行获取，并且进行缓存更新
            PageHelper.startPage(Constants.HOST_QUESTION_LIST_START,Constants.HOST_QUESTION_LIST_END);
             questionHotIds = userSubmitMapper.selectHostQuestionList();
            questionCacheManager.refreshHotQuestionList(questionHotIds);
        }else {
            questionHotIds = questionCacheManager.getHotList();
        }
        return assembleQuestionVOList(questionHotIds);
    }

    private List<QuestionVO> assembleQuestionVOList(List<Long> questionHotIds) {
        //为空返回空列表
        if (CollectionUtil.isEmpty(questionHotIds)) {
            return new ArrayList<>();
        }

        //不为空，就使用Id来获取题目
        List<QuestionVO> questionVOList = new ArrayList<>();
        for (Long questionId : questionHotIds) {
            QuestionVO questionVO = new QuestionVO();
            //通过Id获取题目详情信息 => 目的是获取题目
            QuestionDetailVO questionDetailVO = detail(questionId);
            questionVO.setTitle(questionDetailVO.getTitle());
            questionVOList.add(questionVO);
        }
        return questionVOList;
    }

    private void refreshQuestion() {
        //从数据库查询题目的全部信息
        List<Question> questions = questionMapper.selectList(new LambdaQueryWrapper<>());
        if (CollectionUtil.isEmpty(questions)) {
            //没有数据直接返回
            return;
        }
        //如果有数据，让ES进行同步,因为QuestionRepository中存放的是QuestionES，所以还需要进行类型转化
        List<QuestionES> questionES = BeanUtil.copyToList(questions, QuestionES.class);
        questionRepository.saveAll(questionES);
    }
}
